/*
 * Copyright (c) Forge Development LLC and contributors
 * SPDX-License-Identifier: LGPL-2.1-only
 */

package net.neoforged.neoforge.common.loot;

import com.mojang.datafixers.Products;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import java.util.List;
import java.util.function.Predicate;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.levelgen.feature.trunkplacers.BendingTrunkPlacer;
import net.minecraft.world.level.storage.loot.LootContext;
import net.minecraft.world.level.storage.loot.predicates.AllOfCondition;
import net.minecraft.world.level.storage.loot.predicates.LootItemCondition;

/**
 * A base implementation of a Global Loot Modifier for modders to extend.
 * Takes care of ILootCondition matching and comes with the base codec to extend.
 */
public abstract class LootModifier implements IGlobalLootModifier {
    protected final LootItemCondition[] conditions;
    private final Predicate<LootContext> combinedConditions;

    /**
     * Simplifies codec creation, especially if no other fields are added:
     * <p>
     * {@code
     * public static final Codec<MyLootModifier> CODEC = RecordCodecBuilder.create(inst -> codecStart(inst).apply(inst, MyLootModifier::new));
     * }
     * </p>
     * Otherwise can follow this with #and() to add more fields.
     * Examples: Forge Test Subclasses or {@link BendingTrunkPlacer#CODEC}
     */
    protected static <T extends LootModifier> Products.P1<RecordCodecBuilder.Mu<T>, LootItemCondition[]> codecStart(RecordCodecBuilder.Instance<T> instance) {
        return instance.group(LOOT_CONDITIONS_CODEC.fieldOf("conditions").forGetter(lm -> lm.conditions));
    }

    /**
     * Constructs a LootModifier.
     * 
     * @param conditionsIn the ILootConditions that need to be matched before the loot is modified.
     */
    protected LootModifier(LootItemCondition[] conditionsIn) {
        this.conditions = conditionsIn;
        this.combinedConditions = AllOfCondition.allOf(List.of(conditionsIn));
    }

    @Override
    public final ObjectArrayList<ItemStack> apply(ObjectArrayList<ItemStack> generatedLoot, LootContext context) {
        return this.combinedConditions.test(context) ? this.doApply(generatedLoot, context) : generatedLoot;
    }

    /**
     * Applies the modifier to the generated loot (all loot conditions have already been checked
     * and have returned true).
     * 
     * @param generatedLoot the list of ItemStacks that will be dropped, generated by loot tables
     * @param context       the LootContext, identical to what is passed to loot tables
     * @return modified loot drops
     */

    protected abstract ObjectArrayList<ItemStack> doApply(ObjectArrayList<ItemStack> generatedLoot, LootContext context);
}
